package hc
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import org.codehaus.groovy.control.CompilerConfiguration

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature;

import hc.bre.cmn.ListOption
import hc.bre.cmn.ILoad

@groovy.transform.CompileStatic
class Util {

	static ObjectMapper objectMapper = new ObjectMapper();

	static {
		objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
	}

	static Map jsonRead(String jsonText) {
		return jsonRead(jsonText, Object.class) as Map
	}

	static<T> T jsonRead(String jsonText,Class<T> clazz) {
		return objectMapper.readValue(jsonText, clazz)
	}

	static String jsonWrite(Object object) {
		return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object)
	}

	static Object jsonWriteToFile(Object json, String fileName) {
		String jsonText = jsonWrite(json);
		fileWriteText(jsonText,fileName);
		return jsonText;
	}

	static Map jsonReadFromFile(String fileName) {
		return jsonRead(fileReadText(fileName)) as Map
	}

	static void fileWriteText(String text,String fileName) {
		def desFile = new File(fileName)
		if (!desFile.exists()) {

			if(!desFile.getParentFile().exists()) desFile.getParentFile().mkdirs();

			desFile.createNewFile()
		}

		desFile.withWriter('utf-8'){writer->
			writer.write(text);
			writer.flush();
			writer.close();
			//			writer.finalize();
		}
	}

	static String fileReadText(String fileName) {
		def file = new File(fileName)
		return file.getText()
	}

	static byte[] fileReadBinary(String fileName) {
		def file = new File(fileName)
		return file.getBytes()
	}

	static fileDelete(String fileName) {
		def file = new File(fileName)

		if(file.exists()) file.delete()
	}

	static boolean fileExists(String fileName) {
		new File(fileName).exists()
	}

	static String captureStdOut(Closure closure) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		PrintStream ps = new PrintStream(baos, true, "utf-8");

		java.io.PrintStream oldPs = System.out;
		System.out = ps;
		String content='';
		try {
			closure.call();
			content = new String(baos.toByteArray(), java.nio.charset.StandardCharsets.UTF_8);
			ps.close();
		}
		finally {
			System.out= oldPs
		}

		return content;
	}

	static String stackTraceText(Throwable ex) {
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		ex.printStackTrace(pw);
		return sw.toString();
	}

	static<T extends ILoad> List<T> loadList(Object list,Class<T>clazz) {

		if(list == null) return [];

		if(list instanceof List<Map>) {
			return list.collect(){Map it->
				clazz.getConstructor().newInstance().load(it) as T;
			}
		}
		else {
			throw new Exception('illegal argument of list which should be List<Map> type.' + list)
		}
	}

	static<T> List<T> loadValueList(Object list,Class<T>clazz) {
		if(list == null) return [];

		if(list instanceof List) {
			return list.collect(){it as T}
		}
		else {
			throw new Exception("illegal argument because ${list} is not List<${clazz.getName()}> type.")
		}
	}

	static<T> Set<T> loadValueSet(Object list,Class<T>clazz) {
		Set<T> set = [];
		list?.each { set.add(it as T) };
		return set;
	}

	static<T extends ILoad> T loadBean(Object map,Class<T>clazz) {
		if(map == null) return null;

		if(map instanceof Map) {
			return 	clazz.getConstructor().newInstance().load(map) as T;
		}
		else {
			throw new Exception('illegal argument of list which should be Map type.' + map)
		}
	}

	static Map MapOrEmpty(Object map) {
		if(map == null) return [:];

		if(map instanceof Map) {
			return 	map;
		}
		else {
			throw new Exception('illegal argument which should be Map type.' + map)
		}
	}

	static<T> Map<Integer,T> loadIntegerMap(Object map,Class<T>clazz,boolean ignoreNull = true) {
		if(map == null) return [:];

		LinkedHashMap<Integer,T> resultMap = new LinkedHashMap<Integer,T>();

		//exception if not to convert before using
		Map temp = map as Map;

		if(temp == null)
		{
			throw new Exception('illegal argument which should be Map type.' + map)
		}

		temp.each { k, v ->
			if(ignoreNull && v == null) return;
			resultMap.put(Integer.parseInt(k.toString()),v as T);
		};

		return resultMap;
	}

	public static Method getStaticMethod(Class clazz, String name, Class<?>... parameterTypes) {
		try {
			Method m = clazz.getMethod(name, parameterTypes);
			return Modifier.isStatic(m.modifiers)? m:null;
		}
		catch(Exception ex) {
			return null;
		}
	}

	public static Field getStaticField(Class clazz, String name) {
		try {
			Field m = clazz.getField(name);
			return Modifier.isStatic(m.modifiers)? m:null;
		}
		catch(java.lang.NoSuchFieldException ex) {
			return null;
		}
	}

	public static String esc(String text) {
		if(!text)return '';
		return text.replace("'", "\\'").replace('"', '\\"');
	}

	public static boolean empty(String text) {
		return text == null || text == ''
	}

	public static<T> T clone(T obj)
	{
		if(obj == null) return null;

		return jsonRead(jsonWrite(obj),obj.getClass());
	}


	public static String rpathPackageName(String rpath)
	{
		rpath = rpath.replace('/', '.')
		def dot = rpath.lastIndexOf(".")
		return dot >= 0? rpath.substring(0,dot):""
	}

	public static String rpathClassShortName(String rpath)
	{
		def dot = rpath.lastIndexOf("/")
		return dot >= 0? rpath.substring(dot+1):rpath
	}

	//test/abc/cde --> test
	public static String rpathPrjectCode(String rpath)
	{
		def dot = rpath.indexOf("/")
		return dot >= 0? rpath.substring(0,dot):rpath
	}

	public static String rpathInProject(String rpath)
	{
		def dot = rpath.indexOf("/")
		return dot >= 0? rpath.substring(dot+1):rpath
	}

	public static String rpathClassFullName(String rpath)
	{
		return rpath.replace('/', '.')
	}

	static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss");
	public static String dateTimeNow()
	{
		return dateTimeFormatter.format(LocalDateTime.now());
	}
	public static LocalDateTime dateTimeParse(String datetime)
	{
//		datetime = datetime.replace('T', ' ')
		LocalDateTime.parse(datetime,dateTimeFormatter)
	}
	
}
